/*
 * kmp_utility.c -- Utility routines for the OpenMP support library.
 * $Revision: 42588 $
 * $Date: 2013-08-13 01:26:00 -0500 (Tue, 13 Aug 2013) $
 */

/* <copyright>
    Copyright (c) 1997-2013 Intel Corporation.  All Rights Reserved.

    Redistribution and use in source and binary forms, with or without
    modification, are permitted provided that the following conditions
    are met:

      * Redistributions of source code must retain the above copyright
        notice, this list of conditions and the following disclaimer.
      * Redistributions in binary form must reproduce the above copyright
        notice, this list of conditions and the following disclaimer in the
        documentation and/or other materials provided with the distribution.
      * Neither the name of Intel Corporation nor the names of its
        contributors may be used to endorse or promote products derived
        from this software without specific prior written permission.

    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
    "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
    A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
    HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
    SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
    LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
    DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
    THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
    (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
    OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

</copyright> */

#include "kmp.h"
#include "kmp_wrapper_getpid.h"
#include "kmp_str.h"
#include <float.h>
#include "kmp_i18n.h"

/*#include "kmp_pch.h"*/

/* ------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------ */

#ifdef __cplusplus
EXTERN_C_BEGIN
#endif // __cplusplus

static const char *unknown = "unknown";

#if KMP_ARCH_X86 || KMP_ARCH_X86_64

/* NOTE: If called before serial_initialize (i.e. from runtime_initialize), then */
/* the debugging package has not been initialized yet, and only "0" will print   */
/* debugging output since the environment variables have not been read.          */

static int trace_level = 5;

/*
 * LOG_ID_BITS  = ( 1 + floor( log_2( max( log_per_phy - 1, 1 ))))
 * APIC_ID      = (PHY_ID << LOG_ID_BITS) | LOG_ID
 * PHY_ID       = APIC_ID >> LOG_ID_BITS
 */
KMP_EXTERNC int KMP_CDECL
__kmp_get_physical_id( int log_per_phy, int apic_id )
{
   int index_lsb, index_msb, temp;

   if (log_per_phy > 1) {
	index_lsb = 0;
	index_msb = 31;

	temp = log_per_phy;
        while ( (temp & 1) == 0 ) {
	    temp  >>= 1;
	    index_lsb++;
	}

	temp = log_per_phy;
	while ( (temp & 0x80000000)==0 ) {
	    temp <<= 1;
	    index_msb--;
	}

	/* If >1 bits were set in log_per_phy, choose next higher power of 2 */
	if (index_lsb != index_msb) index_msb++;

	return ( (int) (apic_id >> index_msb) );
   }

   return apic_id;
}


/*
 * LOG_ID_BITS  = ( 1 + floor( log_2( max( log_per_phy - 1, 1 ))))
 * APIC_ID      = (PHY_ID << LOG_ID_BITS) | LOG_ID
 * LOG_ID       = APIC_ID & (( 1 << LOG_ID_BITS ) - 1 )
 */
KMP_EXTERNC int KMP_CDECL
__kmp_get_logical_id( int log_per_phy, int apic_id )
{
   unsigned current_bit;
   int bits_seen;
   unsigned mask;

   if (log_per_phy <= 1) return ( 0 );

   bits_seen = 0;

   for (current_bit = 1; log_per_phy != 0; current_bit <<= 1) {
	if ( log_per_phy & current_bit ) {
	    log_per_phy &= ~current_bit;
	    bits_seen++;
	}
   }

   /* If exactly 1 bit was set in log_per_phy, choose next lower power of 2 */
   if (bits_seen == 1) {
	current_bit >>= 1;
   }

   return ( (int) ((current_bit - 1) & apic_id) );
}


static
kmp_uint64 KMP_CDECL
__kmp_parse_frequency(        // R: Frequency in Hz.
    const char * frequency    // I: Float number and unit: MHz, GHz, or TGz.
) {

    double       value  = 0.0;
    const char * unit   = NULL;
    kmp_uint64   result = ~ 0;

    if ( frequency == NULL ) {
        return result;
    }; // if
    value = strtod( frequency, (char * *) & unit ); // strtod() does not like "char conts *".
    if ( 0 < value && value <= DBL_MAX ) {          // Good value (not overflow, underflow, etc).
        if ( strcmp( unit, "MHz" ) == 0 ) {
            value = value * 1.0E+6;
        } else if ( strcmp( unit, "GHz" ) == 0 ) {
            value = value * 1.0E+9;
        } else if ( strcmp( unit, "THz" ) == 0 ) {
            value = value * 1.0E+12;
        } else {                      // Wrong unit.
            return result;
        }; // if
		result = (kmp_uint64)value;
    }; // if
    return result;

}; // func __kmp_parse_cpu_frequency

KMP_EXTERNC void KMP_CDECL
__kmp_query_cpuid( kmp_cpuinfo_t *p )
{
    struct kmp_cpuid buf;
    int max_arg;
    int log_per_phy;
    int cflush_size;

    p->initialized = 1;

    p->sse2 = 1; // Assume SSE2 by default.

    __kmp_x86_cpuid( 0, 0, &buf );

    KA_TRACE( trace_level, ("INFO: CPUID %d: EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\n",
        0, buf.eax, buf.ebx, buf.ecx, buf.edx ) );

    max_arg = buf.eax;

    p->apic_id = -1;

    if (max_arg >= 1) {
        int i;
        kmp_uint32 t, data[ 4 ];

        __kmp_x86_cpuid( 1, 0, &buf );
        KA_TRACE( trace_level, ("INFO: CPUID %d: EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\n",
                                1, buf.eax, buf.ebx, buf.ecx, buf.edx ) );

        {
#define get_value(reg,lo,mask) ( ( ( reg ) >> ( lo ) ) & ( mask  ) )

            p->signature = buf.eax;
            p->family    =   get_value( buf.eax, 20, 0xff )        + get_value( buf.eax, 8, 0x0f );
            p->model     = ( get_value( buf.eax, 16, 0x0f ) << 4 ) + get_value( buf.eax, 4, 0x0f );
            p->stepping  =   get_value( buf.eax,  0, 0x0f );

#undef get_value

            KA_TRACE( trace_level, (" family = %d, model = %d, stepping = %d\n", p->family, p->model, p->stepping ) );
        }

        for ( t = buf.ebx, i = 0; i < 4; t >>= 8, ++i ) {
            data[ i ] = (t & 0xff);
        }; // for

        p->sse2 = ( buf.edx >> 26 ) & 1;

#ifdef KMP_DEBUG

        if ( (buf.edx >> 4) & 1 ) {
            /* TSC - Timestamp Counter Available */
            KA_TRACE( trace_level, (" TSC" ) );
        }
        if ( (buf.edx >> 8) & 1 ) {
            /* CX8 - CMPXCHG8B Instruction Available */
            KA_TRACE( trace_level, (" CX8" ) );
        }
        if ( (buf.edx >> 9) & 1 ) {
            /* APIC - Local APIC Present (multi-processor operation support */
            KA_TRACE( trace_level, (" APIC" ) );
        }
        if ( (buf.edx >> 15) & 1 ) {
            /* CMOV - Conditional MOVe Instruction Available */
            KA_TRACE( trace_level, (" CMOV" ) );
        }
        if ( (buf.edx >> 18) & 1 ) {
            /* PSN - Processor Serial Number Available */
            KA_TRACE( trace_level, (" PSN" ) );
        }
        if ( (buf.edx >> 19) & 1 ) {
            /* CLFULSH - Cache Flush Instruction Available */
            cflush_size = data[ 1 ] * 8;    /* Bits 15-08: CLFLUSH line size = 8 (64 bytes) */
            KA_TRACE( trace_level, (" CLFLUSH(%db)", cflush_size ) );

        }
        if ( (buf.edx >> 21) & 1 ) {
            /* DTES - Debug Trace & EMON Store */
            KA_TRACE( trace_level, (" DTES" ) );
        }
        if ( (buf.edx >> 22) & 1 ) {
            /* ACPI - ACPI Support Available */
            KA_TRACE( trace_level, (" ACPI" ) );
        }
        if ( (buf.edx >> 23) & 1 ) {
            /* MMX - Multimedia Extensions */
            KA_TRACE( trace_level, (" MMX" ) );
        }
        if ( (buf.edx >> 25) & 1 ) {
            /* SSE - SSE Instructions */
            KA_TRACE( trace_level, (" SSE" ) );
        }
        if ( (buf.edx >> 26) & 1 ) {
            /* SSE2 - SSE2 Instructions */
            KA_TRACE( trace_level, (" SSE2" ) );
        }
        if ( (buf.edx >> 27) & 1 ) {
            /* SLFSNP - Self-Snooping Cache */
            KA_TRACE( trace_level, (" SLFSNP" ) );
        }
#endif /* KMP_DEBUG */

        __kmp_ht_capable = FALSE;
        if ( (buf.edx >> 28) & 1 ) {

            /* HT - Processor is HT Enabled (formerly JT) */
            __kmp_ht_capable = TRUE;

            /* Bits 23-16: Logical Processors per Physical Processor (1 for P4) */
            log_per_phy = data[ 2 ];
            __kmp_ht_log_per_phy = log_per_phy;

            p->apic_id     = data[ 3 ]; /* Bits 31-24: Processor Initial APIC ID (X) */
            KA_TRACE( trace_level, (" HT(%d TPUs)", log_per_phy ) );

            if( log_per_phy > 1 ) {
                /* default to 1k FOR JT-enabled processors (4k on OS X*) */
#if KMP_OS_DARWIN
                p->cpu_stackoffset = 4 * 1024;
#else
                p->cpu_stackoffset = 1 * 1024;
#endif
            }

            p->physical_id = __kmp_get_physical_id( log_per_phy, p->apic_id );
            p->logical_id  = __kmp_get_logical_id( log_per_phy, p->apic_id );
        }
#ifdef KMP_DEBUG
        if ( (buf.edx >> 29) & 1 ) {
            /* ATHROTL - Automatic Throttle Control */
            KA_TRACE( trace_level, (" ATHROTL" ) );
        }
        KA_TRACE( trace_level, (" ]\n" ) );

        for (i = 2; i <= max_arg; ++i) {
            __kmp_x86_cpuid( i, 0, &buf );
            KA_TRACE( trace_level,
                      ( "INFO: CPUID %d: EAX=0x%08X EBX=0x%08X ECX=0x%08X EDX=0x%08X\n",
                        i, buf.eax, buf.ebx, buf.ecx, buf.edx ) );
        }
#endif
#if KMP_USE_ADAPTIVE_LOCKS
        p->rtm = 0;
        if (max_arg > 7)
        {
            /* RTM bit CPUID.07:EBX, bit 11 */
            __kmp_x86_cpuid(7, 0, &buf);
            p->rtm = (buf.ebx >> 11) & 1;
            KA_TRACE( trace_level, (" RTM" ) );
        }
#endif
    }; // if

    { // Parse CPU brand string for frequency.

        union kmp_cpu_brand_string {
            struct kmp_cpuid buf[ 3 ];
            char             string[ sizeof( struct kmp_cpuid ) * 3 + 1 ];
        }; // union kmp_cpu_brand_string
        union kmp_cpu_brand_string brand;
        int i;

        p->frequency = 0;

        // Get CPU brand string.
        for ( i = 0; i < 3; ++ i ) {
            __kmp_x86_cpuid( 0x80000002 + i, 0, &brand.buf[ i ] );
        }; // for
        brand.string[ sizeof( brand.string ) - 1 ] = 0; // Just in case. ;-)
        KA_TRACE( trace_level, ( "cpu brand string: \"%s\"\n", brand.string ) );

        // Parse frequency.
        p->frequency = __kmp_parse_frequency( strrchr( brand.string, ' ' ) );
        KA_TRACE( trace_level, ( "cpu frequency from brand string: %" KMP_UINT64_SPEC "\n", p->frequency ) );
    }
}

#endif /* KMP_ARCH_X86 || KMP_ARCH_X86_64 */

/* ------------------------------------------------------------------------------------ */
/* ------------------------------------------------------------------------------------ */

KMP_EXTERNC void KMP_CDECL
__kmp_expand_host_name( char *buffer, size_t size )
{
    KMP_DEBUG_ASSERT(size >= sizeof(unknown));
#if KMP_OS_WINDOWS
    {
	DWORD	s = size;

	if (! GetComputerNameA( buffer, & s ))
	    strcpy( buffer, unknown );
    }
#else
    buffer[size - 2] = 0;
    if (gethostname( buffer, size ) || buffer[size - 2] != 0)
	strcpy( buffer, unknown );
#endif
}

/* Expand the meta characters in the filename:
 *
 * Currently defined characters are:
 *
 * %H the hostname
 * %P the number of threads used.
 * %I the unique identifier for this run.
 */

KMP_EXTERNC void KMP_CDECL
__kmp_expand_file_name( char *result, size_t rlen, char *pattern )
{
    char	*pos = result, *end = result + rlen - 1;
    char	 buffer[256];
    int		 default_cpu_width = 1;
    int          snp_result;

    KMP_DEBUG_ASSERT(rlen > 0);
    *end = 0;
    {
	int i;
	for(i = __kmp_xproc; i >= 10; i /= 10, ++default_cpu_width);
    }

    if (pattern != NULL) {
	while (*pattern != '\0' && pos < end) {
	    if (*pattern != '%') {
		*pos++ = *pattern++;
	    } else {
		char *old_pattern = pattern;
		int width = 1;
		int cpu_width = default_cpu_width;

		++pattern;

		if (*pattern >= '0' && *pattern <= '9') {
		    width = 0;
		    do {
			width = (width * 10) + *pattern++ - '0';
		    } while (*pattern >= '0' && *pattern <= '9');
		    if (width < 0 || width > 1024)
			width = 1;

		    cpu_width = width;
		}

		switch (*pattern) {
		case 'H':
		case 'h':
		    {
			__kmp_expand_host_name( buffer, sizeof( buffer ) );
			strncpy( pos,  buffer, end - pos + 1);
			if(*end == 0) {
			    while ( *pos )
				++pos;
			    ++pattern;
			} else
			    pos = end;
		    }
		    break;
		case 'P':
		case 'p':
		    {
			snp_result = snprintf( pos, end - pos + 1, "%0*d", cpu_width, __kmp_dflt_team_nth );
			if(snp_result >= 0 && snp_result <= end - pos) {
			    while ( *pos )
				++pos;
			    ++pattern;
			} else
			    pos = end;
		    }
		    break;
		case 'I':
		case 'i':
		    {
			pid_t id = getpid();
			snp_result = snprintf( pos, end - pos + 1, "%0*d", width, id );
			if(snp_result >= 0 && snp_result <= end - pos) {
			    while ( *pos )
				++pos;
			    ++pattern;
			} else
			    pos = end;
			break;
		    }
		case '%':
		    {
			*pos++ = '%';
			++pattern;
			break;
		    }
		default:
		    {
			*pos++ = '%';
			pattern = old_pattern + 1;
			break;
		    }
		}
	    }
	}
	/* TODO: How do we get rid of this? */
	if(*pattern != '\0')
	    KMP_FATAL( FileNameTooLong );
    }

    *pos = '\0';
}


#ifdef __cplusplus
EXTERN_C_END
#endif // __cplusplus
